﻿using CC.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;


namespace CcMath
{

    /// <summary>
    /// Touchstone文件，目前仅支持1.0
    /// </summary>
    public class Touchstone
    {

        public double [] Freqs_MHz
        {
            get
            {
                double[] freqs = new double[mFreqs.Length];
                for (int i = 0; i < freqs.Length; i++)
                {
                    freqs[i] = mFreqs[i] / 1e6;
                }
                return freqs;
            }
        }
        /// <summary>
        /// 数据矩阵，为N*N矩阵，N为端口数
        /// 例如，对s2p文件来说，N=2， mMatrix由4个元素组成：s11,s21,s12,s22， 每个S参数为频率的一维向量
        /// </summary>
        Complex[][] mMatrix;
        /// <summary>
        /// 频率清单,Hz，频率与数据量应一一对应
        /// </summary>
        double[] mFreqs;
      
        /* Use for Noise Measures
       double[][] noiseDataMatrix;
       double[][][] noiseDataMatrixForOver2Ports;*/
        TouchstoneInfo mTouchstoneInfo;
        

       
        /// <summary>
        /// 端口数
        /// </summary>
        public int Nports { get
            {
                return mTouchstoneInfo.Nports;
            } }
        /// <summary>
        /// 1.0版只有一个参考阻抗
        /// </summary>
        public double Reference
        {
            get { return mTouchstoneInfo.References[0]; }
            set
            {
                mTouchstoneInfo.References.Clear();
                mTouchstoneInfo.References.Add(value);
            }
        }
        public Touchstone()
        {
            mTouchstoneInfo = new TouchstoneInfo();
            mTouchstoneInfo.Version = 1.0;
        }

       

        public Touchstone(string filename)
        {
            if (!File.Exists(filename)) filename = "db0630.s2p";
            mTouchstoneInfo = ReadInformationFromFile(filename);
            mMatrix= ReadDataFromFile(filename, out mFreqs); 
            
        }

        public Touchstone(double[] freqs, TouchstoneInfo tinfo, Complex[] s11, Complex[] s21, Complex[] s12, Complex[] s22)
        {
            mTouchstoneInfo = tinfo;
            mFreqs = freqs;
            mMatrix = new Complex[4][];

            mMatrix[0] = s11 ?? Zeros(freqs.Length);
            mMatrix[1] = s21 ?? Zeros(freqs.Length);
            mMatrix[2] = s12 ?? Zeros(freqs.Length);
            mMatrix[3] = s22 ?? Zeros(freqs.Length);
        }
        private Complex[] Zeros(int length)
        {
            Complex[] array = new Complex[length];
            for (int i = 0; i < length; i++)
            {
                array[i] = Complex.Zero;
            }
            return array;
        }
        /// <summary>
        /// 通过检测文件信息来判断是否为touchstone文件
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static bool IsTochstoneFile(string file)
        {
            try
            {
                var tsinfo = ReadInformationFromFile(file);
                return tsinfo.Nports>0;
            }
            catch (Exception)
            {
                return false;
            }
            
            
        }

        /// <summary>
        /// Hz
        /// </summary>
        /// <returns></returns>
        public double[] GetFreqs() { return mFreqs; }
        /// <summary>
        /// 获取端口S参数，如S11，S21
        /// </summary>
        /// <param name="meas"></param>
        /// <param name="fstart">起始频率，Hz</param>
        /// <param name="fstop">结束频率，Hz。如果fstop=0则不做频率筛选</param>
        /// <returns></returns>
        public Complex[] GetSParam(EMeasurements meas, double fstart = 0, double fstop = 0)
        {
            var (src, targ) = TranslateMeasToPort(meas);
            return GetSParam(src, targ, fstart, fstop);
        }
        /// <summary>
        /// 获取端口S参数，如S11，S21
        /// </summary>
        /// <param name="srcPort">源端口，以1为起始，如1，2</param>
        /// <param name="targPort">目的端口，以1为起始，如1，2</param>
        /// <param name="fstart">起始频率，Hz</param>
        /// <param name="fstop">结束频率，Hz。如果fstop=0则不做频率筛选</param>
        /// <returns>复数形式</returns>
        public Complex[] GetSParam(int srcPort, int targPort, double fstart = 0, double fstop = 0)
        {
            int index = mTouchstoneInfo.Nports * (srcPort - 1) + targPort - 1;
            if(fstop==0)
                return mMatrix[index];
            else
            {
                int start = mFreqs.IndexOf(fstart);
                int stop = mFreqs.IndexOf(fstop);
                return mMatrix[index].SubArray(start, stop);
            }
        }
        private (int src,int targ) TranslateMeasToPort(EMeasurements meas)
        {
            int src = 1;
            int targ = 1;
            switch (meas)
            {
                case EMeasurements.S11:
                    break;
                case EMeasurements.S21:
                    src = 1;
                    targ = 2;
                    break;
                case EMeasurements.S12:
                    src = 2;
                    targ = 1;
                    break;
                case EMeasurements.S22:
                    src = 2;
                    targ = 2;
                    break;
                default:
                    break;
            }
            return (src, targ);
        }
        public Complex GetSParamAtFreq(EMeasurements meas,double freq_MHz)
        {
            var (src, targ) = TranslateMeasToPort(meas);
            return GetSParamAtFreq(src, targ, freq_MHz);
        }
        public Complex GetSParamAtFreq(int srcPort, int targPort, double freq_MHz)
        {
            var sps = GetSParam(srcPort, targPort);
            if (freq_MHz < Freqs_MHz.Min()) return 0;
            int index = Freqs_MHz.IndexOf(freq_MHz);
            return sps[index];
        }
        // <summary>
        /// 获取端口S参数，如S11，S21
        /// </summary>
        /// <param name="srcPort">源端口，以1为起始，如1，2</param>
        /// <param name="targPort">目的端口，以1为起始，如1，2</param>
        /// <param name="fstart">起始频率，Hz</param>
        /// <param name="fstop">结束频率，Hz。如果fstop=0则不做频率筛选</param>
        /// <returns>dB形式</returns>
        public double[] GetSParam_DB(int srcPort,int targPort,double fstart=0,double fstop=0)
        {
            var s = GetSParam(srcPort, targPort,fstart,fstop);
            double[] db = new double[s.Length];
            for (int i = 0; i < s.Length; i++)
            {
                db[i] = s[i].Magnitude_dB();
            }
            return db;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="srcPort"></param>
        /// <param name="targPort"></param>
        /// <returns>(频率MHz,dB)</returns>
        public List<Point32> GetDBvsFreq_MHz(int srcPort, int targPort)
        {
            var sp = GetSParam_DB(srcPort, targPort);
            List<Point32> list = new List<Point32>();
            int count = sp.Length < Freqs_MHz.Length ? sp.Length : Freqs_MHz.Length;
            for (int i = 0; i < count; i++)
            {
                list.Add(new Point32((float)Freqs_MHz[i], (float)sp[i]));
            }
            return list;
        }

        /// <summary>
        /// 取S参数幅度线性值
        /// </summary>
        /// <param name="srcPort">从1开始</param>
        /// <param name="targPort">从1开始</param>
        /// <returns></returns>
        public List<Point32> GetMagVsFreq_MHz(int srcPort, int targPort)
        {
            var sp = GetSParam(srcPort, targPort);
            List<Point32> list = new List<Point32>();
           
            for (int i = 0; i < sp.Length; i++)
            {
                list.Add(new Point32((float)Freqs_MHz[i], (float)sp[i].Magnitude));
            }
            return list;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="srcPort"></param>
        /// <param name="targPort"></param>
        /// <returns>(频率MHz,相位deg)</returns>
        public List<Point32> GetPhasvsFreq_DegMHz(int srcPort, int targPort)
        {
            var sp = GetSParam_Deg(srcPort, targPort);
            List<Point32> list = new List<Point32>();
            int count = sp.Length < Freqs_MHz.Length ? sp.Length : Freqs_MHz.Length;
            for (int i = 0; i < count; i++)
            {
                list.Add(new Point32((float)Freqs_MHz[i], (float)sp[i]));
            }
            return list;
        }
        // <summary>
        /// 获取端口S参数的相位，如S11，S21
        /// </summary>
        /// <param name="srcPort">源端口，以1为起始，如1，2</param>
        /// <param name="targPort">目的端口，以1为起始，如1，2</param>
        /// <param name="fstart">起始频率，Hz</param>
        /// <param name="fstop">结束频率，Hz。如果fstop=0则不做频率筛选</param>
        /// <returns>相位deg</returns>
        public double[] GetSParam_Deg(int srcPort, int targPort, double fstart = 0, double fstop = 0)
        {
            var s = GetSParam(srcPort, targPort, fstart, fstop);
            double[] phas = new double[s.Length];
            for (int i = 0; i < s.Length; i++)
            {
                phas[i] = s[i].Phase_deg();
            }
            return phas;
        }
        // <summary>
        /// 获取端口S参数，如S11，S21
        /// </summary>
        /// <param name="srcPort">源端口，以1为起始，如1，2</param>
        /// <param name="targPort">目的端口，以1为起始，如1，1</param>
        /// <param name="fstart">起始频率，Hz</param>
        /// <param name="fstop">结束频率，Hz。如果fstop=0则不做频率筛选</param>
        /// <returns>VSWR形式</returns>
        public double [] GetVswr(int srcPort,int targPort, double fstart = 0, double fstop = 0)
        {
            var s = GetSParam(srcPort, targPort, fstart, fstop);
            double[] swr = new double[s.Length];
            for (int i = 0; i < s.Length; i++)
            {
                if (s[i].Magnitude == 1) swr[i] = 1e9;//如果全反射，则返回一个很大的数,避免除0异常
                else swr[i] = (1 + s[i].Magnitude) / (1 - s[i].Magnitude);
            }
            return swr;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="srcPort"></param>
        /// <param name="targPort"></param>
        /// <returns>（频率_MHz,驻波比）</returns>
        public List<Point32>GetVswrVsFreq(int srcPort, int targPort)
        {
            var sp = GetVswr(srcPort, targPort);
            List<Point32> vs = new List<Point32>();
            int count = sp.Length < Freqs_MHz.Length ? sp.Length : Freqs_MHz.Length;
            for (int i = 0; i < count; i++)
            {
                vs.Add( new    Point32( (float)Freqs_MHz[i], (float)sp[i]));
            }
            return vs;
        }

        private static TouchstoneInfo ReadInformationFromFile(string filename)
        {
            TouchstoneInfo tinfo = new TouchstoneInfo();
            //从文件名中提取端口数
            string extName = filename.Substring(filename.LastIndexOf('.') + 1);
            extName = extName.Substring(1);
            extName = extName.Substring(0, extName.Length - 1);
            if (int.TryParse(extName, out int nports))
                tinfo.Nports = nports;

            string[] lines = File.ReadAllLines(filename);
            int startLineOfOption = 0;            
            int i = 0;
            string[] optStrs;
            while (i<lines.Length)
            {
                string s = lines[i].Trim();
                if(s.Length>0)
                {
                    if (s[0] == '!'&&startLineOfOption==0)
                    {
                        tinfo.CommentList.Add(s.Substring(1));                        
                    }
                    else if(char.IsDigit(s[0]))
                    {
                        if (s.Contains('!')) s = s.Substring(0, s.IndexOf('!'));
                        string[] ss = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        if (ss.Length % 2 == 1) tinfo.NFreqs++;
                    }
                    else if (s[0] == '#')
                    {//#line, get  base options
                        startLineOfOption = i;//记录option line的位置
                        optStrs = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        for (int j=0;j<optStrs.Length;j++)
                        {
                            switch (optStrs[j].ToUpper())
                            {
                                case "MHZ":
                                    tinfo.FreqUnit = eFreqUnits.MHz;
                                    break;
                                case "KHZ":
                                    tinfo.FreqUnit = eFreqUnits.KHz;
                                    break;
                                case "HZ":
                                    tinfo.FreqUnit = eFreqUnits.Hz;
                                    break;
                                case "GHZ":
                                    tinfo.FreqUnit = eFreqUnits.GHz;
                                    break;
                                case "MA":
                                    tinfo.DataFormat = EFormats.MA;
                                    break;
                                case "RI":
                                    tinfo.DataFormat = EFormats.RI;
                                    break;
                                case "DB":
                                    tinfo.DataFormat = EFormats.DB;
                                    break;
                                case "R":
                                    tinfo.References[0] = double.Parse(optStrs[j + 1]);
                                    j++;
                                    break;

                            }
                        }
                    }
                    else if (s[0] == '['&&tinfo.Version>=2.0) //Handle V2.0 Keywords
                    {
                        optStrs = s.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
                        if (optStrs[0] == "Version") tinfo.Version = double.Parse(optStrs[1]);
                        else if (optStrs[0] == "Number of Ports") tinfo.Nports = int.Parse(optStrs[1]);
                        else if (optStrs[0] == "Number of Frequencies") tinfo.NFreqs = int.Parse(optStrs[1]);
                        else if (optStrs[0] == "Number of Noise Frequencies") tinfo.NNoiseFreqs = int.Parse(optStrs[1]);
                        else if (optStrs[0] == "Matrix Format")
                        {
                            switch (optStrs[1].ToLower())
                            {
                                case "full":
                                    tinfo.MatrixFormat = eMatrixFormats.Full;
                                    break;
                                case "lower":
                                    tinfo.MatrixFormat = eMatrixFormats.LimitMin;
                                    break;
                                case "upper":
                                    tinfo.MatrixFormat = eMatrixFormats.LimitMax;
                                    break;
                            }
                        }
                        else if (optStrs[0] == "Two-Port Data Order")
                        {
                            if (optStrs[1] == "21_12")
                                tinfo.TwoPortDataOrder = eTwoPortDataOrders._21_12;
                            else
                                tinfo.TwoPortDataOrder = eTwoPortDataOrders._12_21;
                        }
                        else if (optStrs[0] == "Reference")
                        {//读入参考特性阻抗参数
                            
                            List<double> refValues = new List<double>();
                            if (optStrs.Length > 1)
                            {
                                string[] vstrs = optStrs[1].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                                foreach (string v in vstrs)
                                {
                                    refValues.Add(double.Parse(v));
                                }
                                if (refValues.Count < tinfo.Nports)
                                {
                                    vstrs = lines[i + 1].Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                                    if (vstrs.Length < tinfo.Nports - refValues.Count)//参考特性阻抗数量小于端口数，则读取失败
                                    {
                                        throw (new Exception("Get Reference Failue： Number of reference impedance LESS THAN Number of Ports!"));
                                    }
                                    foreach (string v in vstrs)
                                    {
                                        refValues.Add(double.Parse(v));
                                    }
                                }
                            }
                            tinfo.References = refValues.ToList();
                        }
                    }
                }
                i++;   
            }
            return tinfo;
        }
        /// <summary>
        /// 从touchstone文件中读取数据，适用于端口数在2以内的文件
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="freqs"></param>
        /// <returns></returns>
        private static Complex[][] ReadDataFromFile(string filename, out double[] freqs)
        {
            TouchstoneInfo tinfo = ReadInformationFromFile(filename);
            freqs = null;
            if (tinfo == null) return null;
            List<double> freqList = new List<double>();
            //初始化S参数矩阵,先用list<complex来接收读取到的数据，返回前再转换
            List<List<Complex>> list = new List<List<Complex>>();
            for (int i = 0; i < tinfo.Nports*tinfo.Nports; i++)
            {
                list.Add(new List<Complex>());
            }
            double fScale = 1;
            switch (tinfo.FreqUnit)
            {
                case eFreqUnits.GHz:
                    fScale = 1e9;
                    break;
                case eFreqUnits.MHz:
                    fScale = 1e6;
                    break;
                case eFreqUnits.KHz:
                    fScale = 1e3;
                    break;
                case eFreqUnits.Hz:
                    break;
                default:
                    break;
            }
            string[] lines = File.ReadAllLines(filename);
            for (int n = 0; n < lines.Length; n++)
            {
                string s = lines[n].Trim();

                if (char.IsDigit(s[0])) //首字为数字，数据矩阵开始
                {
                    if (s.Contains("!")) s = s.Substring(0, s.IndexOf("!"));//去掉注释
                    string[] vstrs = s.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                    bool ok = true;//频率，所有S参数转换成功，才添加一行
                    double freq=0;//每行的频率暂存
                    Complex[] cDatas = new Complex[tinfo.Nports * tinfo.Nports];//每行的数据暂存
                    if (vstrs.Length==tinfo.Nports*tinfo.Nports*2+1)//每行的数据量为1+N*N*2 , [频率, s11实部，s11虚部，s21实部，s21虚部...]
                    {
                        if( double.TryParse(vstrs[0],out  freq))
                        {
                            for (int i = 0; i < vstrs.Length/2; i++)
                            {
                                if(double.TryParse(vstrs[i*tinfo.Nports+1],out double real)&& double.TryParse(vstrs[i * tinfo.Nports +2], out double imag))
                                {
                                    switch (tinfo.DataFormat)
                                    {
                                        case EFormats.DB:
                                            double mag = Math.Pow(10, real / 20f);
                                            double phas = imag.ToRad();
                                            cDatas[i] = new Complex(mag * Math.Cos(phas), mag * Math.Sin(phas));
                                            break;
                                        case EFormats.MA:
                                            double ang = imag.ToRad();
                                            cDatas[i] = new Complex(real * Math.Cos(ang), real * Math.Sin(ang));
                                            break;
                                        case EFormats.RI:
                                            cDatas[i] = new Complex(real, imag);
                                            break;
                                        default:
                                            break;
                                    }
                                }
                                else
                                {
                                    ok = false;
                                }
                            }
                        }
                        else
                        {
                            ok = false;
                        }
                        
                    }
                    else
                    {
                        ok = false;
                    }
                    if(ok)
                    {
                        freqList.Add(freq * fScale);
                        for (int i = 0; i < cDatas.Length; i++)
                        {
                            list[i].Add(cDatas[i]);
                        }
                    }
                    
                }

            }
            //转换为List<complex[]>
            freqs = freqList.ToArray();

            Complex[][] sMatrix = new Complex[list.Count][];
            for (int i = 0; i < sMatrix.Length; i++)
            {
                sMatrix[i] = list[i].ToArray();
            }
            
            return sMatrix;
        }
       
        
        public void WriteToFile(string filename)
        {
            if(mTouchstoneInfo.Version>=2)
            {
                WriteV2File(filename);
            }
            else
            {
                WriteV1File(filename);
            }
        }
        private void WriteV1File(string filename)
        {
            List<string> lines = new List<string>();
            if(mTouchstoneInfo.CommentList.Count>0)
            {// Write Comments
                foreach (string s in mTouchstoneInfo.CommentList)
                {
                    lines.Add( "!" + s);
                }
            }
            //double fscale = 1;
            //switch (mTouchstoneInfo.FreqUnit)
            //{
            //    case eFreqUnits.GHz:
            //        fscale = 1e9;
            //        break;
            //    case eFreqUnits.MHz:
            //        fscale = 1e6;
            //        break;
            //    case eFreqUnits.KHz:
            //        fscale = 1e3;
            //        break;
            //    case eFreqUnits.Hz:
            //        break;
            //    default:
            //        break;
            //}
            //Add Option Line
            string line = "# " + mTouchstoneInfo.FreqUnit.ToString()
                + " " + mTouchstoneInfo.Parameter.ToString()
                + " " + mTouchstoneInfo.DataFormat.ToString();
            if (mTouchstoneInfo.References.Count > 0)
                line += " R " + Reference;
            lines.Add(line);
            if (!string.IsNullOrEmpty(mTouchstoneInfo.DataColTitles))
            {//写数据标题行
                lines.Add(mTouchstoneInfo.DataColTitles);
            }
            for (int i = 0; i < mFreqs.Length; i++)
            {   
                line = (mFreqs[i]).ToString();
                for (int j = 0; j < mMatrix.Length; j++)
                {
                    switch (mTouchstoneInfo.DataFormat)
                    {
                        case EFormats.DB:
                            line += "  " + mMatrix[j][i].Magnitude_dB() + "  " + mMatrix[j][i].Phase_deg();
                            break;
                        case EFormats.MA:
                            line += "  " + mMatrix[j][i].Magnitude + "  " + mMatrix[j][i].Phase_deg();
                            break;
                        case EFormats.RI:
                         
                        default:
                            line += "  " + mMatrix[j][i].Real + "  " + mMatrix[j][i].Imaginary;
                            break;
                    }
                }
                lines.Add(line);
            }
            File.WriteAllLines(filename, lines);
        }
        private void WriteV2File(string filename)
        {
           
        }
    }
    /// <summary>
    /// Touchstone配置信息
    /// </summary>
    public class TouchstoneInfo
    {
        public List<string> CommentList { get; set; }
        /// <summary>
        /// 数据列标题,以空格分开
        /// </summary>
        public string DataColTitles;//
        /// <summary>
        /// V2.0以上有用
        /// </summary>
        public int Nports { get; set; }//Required in V2.0, not permitted in v1.0 files
        // The following shall appear after Nports
        public eFreqUnits FreqUnit { get; set; }
        /// <summary>
        /// 参数名称
        /// </summary>
        public eParameters Parameter { get; set; }
        /// <summary>
        /// 数据格式，S21_S12,还是S12_S21
        /// </summary>
        public EFormats DataFormat { get; set; }
        /// <summary>
        /// 参考阻抗，1.0版只有一个值，2.0版可以有多个值，与端口数对应
        /// </summary>
        public List< double> References { get; set; }//
        
        /// <summary>
        /// 版本，1.0文件不允许
        /// </summary>
        public double Version { get; set; } //not permitted in v1.0 files    
        /// <summary>
        /// 双端口参数的排序，比如可以排成S11,S12,S21,S22
        /// 1.0文件中不允许
        /// </summary>
        public eTwoPortDataOrders TwoPortDataOrder { get; set; }
        /// <summary>
        /// 频率点数，1.0文件不用
        /// </summary>
        public int NFreqs { get; set; }
        /// <summary>
        /// 噪声频率点，1.0文件不用
        /// </summary>
        public int NNoiseFreqs { get; set; } //not permitted in v1.0 files
        /// <summary>
        /// 矩阵格式，1.0不用
        /// </summary>
        public eMatrixFormats MatrixFormat { get; set; }
        public TouchstoneInfo()
        {
            CommentList = new List<string>();
            FreqUnit = eFreqUnits.Hz;
            Parameter = eParameters.S;
            DataFormat = EFormats.MA;
            Version = 1.0;
            TwoPortDataOrder = eTwoPortDataOrders._21_12;
            MatrixFormat = eMatrixFormats.Full;
            References = new List<double>
            {
                50//默认为一个参考阻抗50ohm
            };
        }
        public TouchstoneInfo(IEnumerable<string> comments)
        {
            CommentList = comments.ToList();
            FreqUnit = eFreqUnits.Hz;
            Parameter = eParameters.S;
            DataFormat = EFormats.MA;
            Version = 1.0;
            TwoPortDataOrder = eTwoPortDataOrders._21_12;
            MatrixFormat = eMatrixFormats.Full;
            References = new List<double>
            {
                50//默认为一个参考阻抗50ohm
            };
        }
        
    }
    public enum EMeasurements { S11, S21, S12, S22 }
    public enum eFreqUnits {Unkown, GHz, MHz, KHz, Hz }//表示适于参数，时间为秒,GHz[default]
    public enum eParameters { S, Y, Z, H, G }//{scattering[default],Admittance ,impedance,hibrid-H,hibrid-g}
    public enum EFormats {Unkown, DB, MA, RI }//{magnitude-angle[default],dB-angle,real-imaginary}
    public enum eMatrixFormats { Full, LimitMin, LimitMax }//全矩阵数据，下矩阵数据，上矩阵数据，1.0不支持
    public enum eTwoPortDataOrders { _12_21, _21_12, NotDefine }//数据顺序，1.0不支持
}
